#include "OgreStableHeaders.h"

#include "OgreMpq.h"

#include <StormLib/StormLib.h>


namespace Ogre {

	MPQArchive::MPQArchive( const String& name, const String& archType )
	 : Archive(name, archType)
	 , mhMPQ(NULL)
	{

	}

	MPQArchive::~MPQArchive()
	{
		unload();
	}

	void MPQArchive::load()
	{
		if (!mhMPQ)
		{		
			if (!SFileOpenArchive(mName.c_str(), 0, 0, &mhMPQ))
			{
				checkMPQError(GetLastError(), "opening archive");
			}			

			SFILE_FIND_DATA searchData;
			HANDLE searchHandle = SFileFindFirstFile(mhMPQ, "*", &searchData, NULL);
			if (NULL == searchHandle)
			{
				return;
			}
			while (NULL != searchHandle)
			{
				FileInfo info;
				info.archive = this;
				// Get basename / path
				StringUtil::splitFilename(searchData.cFileName, info.basename, info.path);
				info.filename = searchData.cFileName;
				// Get sizes
				info.compressedSize = static_cast<size_t>(searchData.dwCompSize);
				info.uncompressedSize = static_cast<size_t>(searchData.dwFileSize);
				// folder entries
				if (info.basename.empty())
				{
					info.filename = info.filename.substr (0, info.filename.length () - 1);
					StringUtil::splitFilename(info.filename, info.basename, info.path);
					// Set compressed size to -1 for folders; anyway nobody will check
					// the compressed size of a folder, and if he does, its useless anyway
					info.compressedSize = size_t (-1);
				}

				mFileList.push_back(info);

				if(!SFileFindNextFile(searchHandle, &searchData))
				{
					break;
				}
			}
		}
	}

	void MPQArchive::unload()
	{
		if(mhMPQ)
		{
			if (!SFileCloseArchive(mhMPQ))
			{
				checkMPQError(GetLastError(), "close archive");
			}
			mhMPQ = NULL;
		}
	}

	DataStreamPtr MPQArchive::open( const String& filename ) const
	{
		HANDLE hFile = NULL;
		if(!SFileOpenFileEx(mhMPQ, filename.c_str(), 0, &hFile))
		{
			checkMPQError(GetLastError(), "open file");
			return DataStreamPtr();
		}
		DWORD dwFileSize = 0;
		if (!SFileGetFileSize(hFile, &dwFileSize))
		{
			checkMPQError(GetLastError(), "get file size");
			return DataStreamPtr();
		}
		
		return DataStreamPtr(OGRE_NEW MPQDataStream(filename, hFile, dwFileSize));
	}

	StringVectorPtr MPQArchive::list( bool recursive /*= true*/, bool dirs /*= false*/ )
	{
		StringVectorPtr ret = StringVectorPtr(OGRE_NEW_T(StringVector, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);

		//FileInfoList::iterator i, iend;
		//iend = mFileList.end();
		//for (i = mFileList.begin(); i != iend; ++i)
		//	if ((dirs == (i->compressedSize == size_t (-1))) &&
		//		(recursive || i->path.empty()))
		//		ret->push_back(i->filename);

		return ret;
	}

	FileInfoListPtr MPQArchive::listFileInfo( bool recursive /*= true*/, bool dirs /*= false*/ )
	{
		FileInfoList* fil = OGRE_NEW_T(FileInfoList, MEMCATEGORY_GENERAL)();
		//FileInfoList::const_iterator i, iend;
		//iend = mFileList.end();
		//for (i = mFileList.begin(); i != iend; ++i)
		//	if ((dirs == (i->compressedSize == size_t (-1))) &&
		//		(recursive || i->path.empty()))
		//		fil->push_back(*i);

		return FileInfoListPtr(fil, SPFM_DELETE_T);
	}

	StringVectorPtr MPQArchive::find( const String& pattern, bool recursive /*= true*/, bool dirs /*= false*/ )
	{
		StringVectorPtr ret = StringVectorPtr(OGRE_NEW_T(StringVector, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);
		// If pattern contains a directory name, do a full match
		bool full_match = (pattern.find ('/') != String::npos) ||
			(pattern.find ('\\') != String::npos);

		//FileInfoList::iterator i, iend;
		//iend = mFileList.end();
		//for (i = mFileList.begin(); i != iend; ++i)
		//{
		//	if ((dirs == (i->compressedSize == size_t (-1))) 
		//		&& (recursive || full_match || i->path.empty()))
		//	{
		//		// Check basename matches pattern (zip is case insensitive)
		//		if (StringUtil::match(full_match ? i->filename : i->basename, pattern, false))
		//			ret->push_back(i->filename);
		//	}
		//}

		SFILE_FIND_DATA searchData;
		HANDLE searchHandle = SFileFindFirstFile(mhMPQ, "*", &searchData, NULL);
		if (NULL == searchHandle)
		{
			return ret;
		}
		while (NULL != searchHandle)
		{
			ret->push_back(searchData.cFileName);
			if(!SFileFindNextFile(searchHandle, &searchData))
			{
				break;
			}
		}
		

		return ret;
	}

	bool MPQArchive::exists( const String& filename )
	{
		return false;
	}

	time_t MPQArchive::getModifiedTime( const String& filename )
	{
		struct stat tagStat;
		bool ret = (stat(mName.c_str(), &tagStat) == 0);

		if (ret)
		{
			return tagStat.st_mtime;
		}
		else
		{
			return 0;
		}
	}

	FileInfoListPtr MPQArchive::findFileInfo( const String& pattern, bool recursive /*= true*/, bool dirs /*= false*/ )
	{
		FileInfoListPtr ret = FileInfoListPtr(OGRE_NEW_T(FileInfoList, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);
		// If pattern contains a directory name, do a full match
		bool full_match = (pattern.find ('/') != String::npos) ||
			(pattern.find ('\\') != String::npos);

		//FileInfoList::iterator i, iend;
		//iend = mFileList.end();
		//for (i = mFileList.begin(); i != iend; ++i)
		//	if ((dirs == (i->compressedSize == size_t (-1))) &&
		//		(recursive || full_match || i->path.empty()))
				// Check name matches pattern (zip is case insensitive)
		//		if (StringUtil::match(full_match ? i->filename : i->basename, pattern, false))
		//			ret->push_back(*i);

		return ret;
	}

	void MPQArchive::checkMPQError( int nError, const String& operation ) const
	{
		
	}

	//-----------------------------------------------------------------------
	//-----------------------------------------------------------------------
	//-----------------------------------------------------------------------

	MPQDataStream::MPQDataStream( HANDLE hFile, size_t uncompressedSize )
		: mhFile(hFile)
	{
		mSize = uncompressedSize;
	}

	MPQDataStream::MPQDataStream( const String& name, HANDLE hFile, size_t uncompressedSize )
		: DataStream(name)
		, mhFile(hFile)
	{
		mSize = uncompressedSize;
	}

	//-----------------------------------------------------------------------

	MPQDataStream::~MPQDataStream()
	{
		close();
	}

	//-----------------------------------------------------------------------

	size_t MPQDataStream::read( void* buf, size_t count )
	{
		DWORD dwRead = 0;
		if(!SFileReadFile(mhFile, buf, (DWORD)count, &dwRead))
		{			
			//OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
			//	mName+" - error from StormLib: "+GetLastError(),
			//	"MPQDataStream::read");
		}
		return (size_t)dwRead;
	}

	void MPQDataStream::skip( long count )
	{

	}

	void MPQDataStream::seek( size_t pos )
	{

	}

	size_t MPQDataStream::tell( void ) const
	{
		return (size_t)SFileSetFilePointer(mhFile, 0, NULL, FILE_CURRENT);
	}

	bool MPQDataStream::eof( void ) const
	{
		DWORD pos = SFileSetFilePointer(mhFile, 0, NULL, FILE_CURRENT);
		DWORD size = SFileGetFileSize(mhFile);
		return pos>=size;
	}

	void MPQDataStream::close( void )
	{
		if (!mhFile)
		{
			if (!SFileCloseFile(mhFile))
			{
				
			}
		}		
	}

	//-----------------------------------------------------------------------
	//-----------------------------------------------------------------------
	//-----------------------------------------------------------------------

	const String& MPQArchiveFactory::getType( void ) const
	{
		static String name = "MPQ"; 
		return name;
	}
}